#ifdef USE_SQL
#include <string.h>

#include "lib.h"
#include "TriCardDefs.h"
#include "tcpserv.h"
#include "tcpconn.h"
#include "tcphand.h"
#include "tcpsql.h"
#include "ThreeCardRummySQL.h"
#include "tcpsaved.h"
#include "tcpgame.h"
#include "tcpproto.h"
#include "stdncerr.h"
#include "opts.h"
#include "log.h"
#include "badpacketmsgs.h"
#include "XMLTranslator.h"

const int32 ThreeCardConnection::VERSION1_HEADER_SIZE = 6;

#define APP1		(uint8)'T'
#define APP2		(uint8)'C'

static TrackSQL* tracker = NULL;

void
ThreeCardServer::startTracker()
{
	ServerOpts::load();

	tracker = new TrackSQL(ServerOpts::dbdsn, ServerOpts::dblogin, ServerOpts::dbpasswd);

	// CSN-8891
	webServiceCall = new WebServiceCall();
}

void
ThreeCardServer::stopTracker()
{
	delete tracker;
	tracker = 0;

	delete webServiceCall;
	webServiceCall = 0;
}

bool8
ThreeCardServer::initSuccess()
{
	if (tracker && tracker->initOK())
		return Server::initSuccess();
	else
	{
		Log::msgError("Failed to connect to the database.  Aborting.");
		return false;
	}
}

bool8
ThreeCardConnection::parseCasinoLogin(int32 stdReq, int32* Err)
{
	TInfo *	info;
	char	login[PIDLEN + 1] = { 0 };
	int32	i;

	if (msgbuf[1] != APP1 || msgbuf[2] != APP2 ||
	    ((msgbuf[3]!= PROTO_VERSION1 || msgbuf[4] != PROTO_VERSION2) && // only accept the current version and version 1.3.
         !(msgbuf[3]==1 && msgbuf[4]==3)))

	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c  ",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4]));
		return false;
	}

	info = new TInfo();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

    if(msgbuf[3]==1)    // if this is version 1, then we're dealing with an older client...humor it.
    {
        i = VERSION1_HEADER_SIZE - 1;   // the ansi header is 1 byte smaller than the unicode header...
    }
    else
    {
        i = stdReq;
    }

	memcpy(login, msgbuf + i, PIDLEN);
	login[PIDLEN] = 0;
	i += PIDLEN;

	strlwr(login);
	info->login = login;

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{			
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}

    // kharmon.  01-31-06.  #5862.  This connection now supports both the 3 card poker AND rummy.
    // So, create the appropriate game object, based on the client's login message.
    if(msgbuf[4]==1)
    {
        // if this is version 1, then we're dealing with an older client...humor it.
        m_eGameId = TRICARD_POKER;
        m_pDBInterface = new ThreeCardPokerSQL(tracker, info);
    }
    else
    {
        switch(msgbuf[5])
        {
            case TRICARD_POKER:
            {
                m_eGameId = TRICARD_POKER;
                m_pDBInterface = new ThreeCardPokerSQL(tracker, info);
                break;
            }
            case TRICARD_RUMMY:
            {
                m_eGameId = TRICARD_RUMMY;
                m_pDBInterface = new ThreeCardRummySQL(tracker, info);
                break;
            }
            default:
            {
		        *Err = LOGINERR_CONTACTCUSTSERVICE;
		        DEBUGMSG(("Login: invalid game id: %d", (int) m_eGameId));
                Log::msgError("  Login: invalid game id.  ");
                delete info;
		        return false;
            }
        }
    }

	if (tracker)
	{
		int32 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}

	info->pid = info->login;
    
	/*
	if (tracker)
	{
		int32 result;

		tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId,
					info->pid);
		tracker->unlock();

		if (result != TrackSQL::NoError)
		{
			if(result == TrackSQL::AdminLogout)
				*Err = LOGINERR_ADMINLOGOUT;
			else if(result == TrackSQL::AutoLogout)
				*Err = LOGINERR_AUTOLOGOUT;
			else
				*Err = LOGINERR_NOTLOGGEDIN;

            DEBUGMSG(("Login: attempt to play without logging in: %S",
					(const uint16*) info->login));
            delete info;
            return false;
		}
	}
	*/

	memset(info->gid, 0xff, sizeof(ThreeCardGameID));

	casinoSpecific = info;
    #ifdef FORCEDSHOE 
    m_pid=info->login;
    #endif


	return true;
}

bool8
ThreeCardConnection::parseUnicodeCasinoLogin(int32 stdReq, int32* Err)
{
	TInfo *	info;
	int32	i;

	if (msgbuf[2] != APP1 || msgbuf[3] != APP2 ||
	    ((msgbuf[4]!= PROTO_VERSION1 || msgbuf[5] != PROTO_VERSION2) && // only accept the current version and version 1.3.
         !(msgbuf[4]==1 && msgbuf[5]==3)))
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c  ",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4]));
		return false;
	}

	info = new TInfo();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}	

    if(msgbuf[4]==1)    // if this is version 1, then we're dealing with an older client...humor it.
    {
        i = VERSION1_HEADER_SIZE;
    }
    else
    {
        i = stdReq;
    }

	i += info->login.deserialize(msgbuf + i);
	info->login.toLower();

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{		
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}

    // kharmon.  01-31-06.  #5862.  This connection now supports both the 3 card poker AND rummy.
    // So, create the appropriate game object, based on the client's login message.
    if(msgbuf[4]==1)    
    {
        // if this is version 1, then we're dealing with an older client...humor it.
        m_eGameId = TRICARD_POKER;
        m_pDBInterface = new ThreeCardPokerSQL(tracker, info);
    }
    else
    {
        switch(msgbuf[6])
        {
            case TRICARD_POKER:
            {
                m_eGameId = TRICARD_POKER;
                m_pDBInterface = new ThreeCardPokerSQL(tracker, info);
                break;
            }
            case TRICARD_RUMMY:
            {
                m_eGameId = TRICARD_RUMMY;
                m_pDBInterface = new ThreeCardRummySQL(tracker, info);
                break;
            }
            default:
            {
		        *Err = LOGINERR_CONTACTCUSTSERVICE;
                delete info;
		        DEBUGMSG(("Login: invalid game id: %d", (int) m_eGameId));
                Log::msgError("  Login: invalid game id.  ");
		        return false;
            }
        }
    }

	if (tracker)
	{
		int32 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}

	info->pid = info->login;

	/*
	if (tracker)
	{
		int32 result;

		tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId,info->pid);
		tracker->unlock();

		if (result != TrackSQL::NoError)
		{
			if(result == TrackSQL::AdminLogout)
				*Err = LOGINERR_ADMINLOGOUT;
			else if(result == TrackSQL::AutoLogout)
				*Err = LOGINERR_AUTOLOGOUT;
			else
				*Err = LOGINERR_NOTLOGGEDIN;

            DEBUGMSG(("Login: attempt to play without logging in: %S",
					(const uint16*) info->login));
            delete info;
            return false;
		}
	}
	*/

	memset(info->gid, 0xff, sizeof(ThreeCardGameID));

	casinoSpecific = info;

    #ifdef FORCEDSHOE 
    m_pid=info->login;
    #endif


	return true;
}

void
ThreeCardConnection::trackaddBadPacket(int32 error_reason, char MessageType, char *msgbuf, int32 msglen)
{
	TInfo *info = (TInfo *) casinoSpecific;

	if (tracker && info)
	{
		tracker->lock();
		tracker->addBadPacket(m_eGameId, 0,
			sBadPacketMessages[error_reason].message,
			sBadPacketMessages[error_reason].severity,
			info->pid, MessageType, (uint8 *)msgbuf, msglen);
		tracker->unlock();
	}
}

int32
ThreeCardConnection::trackValidateLogin()
{
    TInfo * info = (TInfo *)casinoSpecific;
    int32 result = TrackSQL::ExecFailed;

    if (tracker && info)
    {
        tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId, info->pid);
        tracker->unlock();
    }

    return result;
}

void ThreeCardConnection::trackLogoutSession()
{
	TInfo * info = (TInfo *)casinoSpecific;
	
	if (tracker && info && !info->login.isEmpty())
    {
        tracker->lock();
		tracker->logout(ServerOpts::casinoId, info->login, info->forMoney, String::emptyString, info->sessionId);
        tracker->unlock();
    }
}

int32
ThreeCardConnection::trackValidateSession()
{
    TInfo * info = (TInfo *)casinoSpecific;
    int32 result = TrackSQL::ExecFailed;

    if (tracker && info)
    {
        tracker->lock();
		result = tracker->validateSession(info->pid, info->sessionId);
        tracker->unlock();
    }

    return result;
}

void
ThreeCardConnection::removeCasinoSpecific()
{
	delete (TInfo *)casinoSpecific;

	casinoSpecific = (void*)0;
}

int32
ThreeCardConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
ThreeCardConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

#else	// USE_SQL

#include <string.h>

#include "lib.h"
#include "tcpserv.h"
#include "tcpconn.h"
#include "tcpver.h"
#include "stdncerr.h"

#define APP1		(uint8)'T'
#define APP2		(uint8)'C'

typedef struct _info
{
	int32 balance;
} Info, *pInfo;

void ThreeCardServer::startTracker() { }
void ThreeCardServer::stopTracker() { }
bool8 ThreeCardServer::initSuccess() { return Server::initSuccess(); }
void ThreeCardConnection::trackaddBadPacket(int32 error_reason, char MessageType, char *msgbuf, int32 msglen) {}

bool8
ThreeCardConnection::parseCasinoLogin(int32 /*stdReq*/, int32* Err)
{
	if (msgbuf[1]	!= APP1				|| msgbuf[2] != APP2 ||
		msgbuf[3]	!= PROTO_VERSION1	|| msgbuf[4] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		return false;
	}

	Info* info = new Info();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	info->balance = 100000;		// $1000.00 to start with

	casinoSpecific = info;

	return true;
}
void
ThreeCardConnection::removeCasinoSpecific()
{
	delete (pInfo)casinoSpecific;

	casinoSpecific = (void*)0;
}

int32
ThreeCardConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
ThreeCardConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
ThreeCardConnection::trackGetBalance()
{
	pInfo info = (pInfo)casinoSpecific;
	if (info)
		return info->balance;
	else
		return 0;
}

int32
ThreeCardConnection::trackAdjustBalance(int32 Amount)
{
	pInfo info = (pInfo)casinoSpecific;
	if (info)
	{
		info->balance += Amount;

		return info->balance;
	}
	else
		return 0;
}

bool8
ThreeCardConnection::trackGetPlayerLimits(int32* minBet, int32* maxBet)
{
	*minBet = 500;
	*maxBet = 20000;
	return true;
}

bool8
ThreeCardConnection::trackNewGame(int32 Bet, int32* Balance)
{
	pInfo info = (pInfo)casinoSpecific;

	if (info)
		*Balance = trackAdjustBalance(-Bet);
	else
		*Balance = 0;

	return true;
}

bool8
ThreeCardConnection::trackDeal(CStudHand* /*Dealer*/, CStudHand* /*Player*/)
{
	return true;
}

bool8
ThreeCardConnection::trackGameDone(char /*Raised*/,
	int32 /*PayAnte*/, int32 /*PayRaise*/, int32 /*Outcome*/,
	int32 BalAdjust, int32 *Balance)
{
	pInfo info = (pInfo)casinoSpecific;

	if (info)
		*Balance = trackAdjustBalance(BalanceAdjust);
	else
		*Balance = 0;

	return true;
}

bool8 ThreeCardConnection::trackHasSavedGame(int32* /*err*/) { return false; }

bool8
ThreeCardConnection trackGetOptions();
{
	return true;
}


#endif
